package app.vendorportal.util;

import app.vendorportal.utils.DateFormatter;
import jxl.format.Alignment;
import jxl.write.DateFormat;
import jxl.write.DateTime;
import jxl.write.Label;
import jxl.write.Number;
import jxl.write.WritableCell;
import jxl.write.WritableCellFormat;
import jxl.write.WritableHyperlink;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import jxl.write.biff.RowsExceededException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.view.document.AbstractJExcelView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

public class ViewExcel extends AbstractJExcelView {
    private final Logger logger = LoggerFactory.getLogger(ViewExcel.class);

    @SuppressWarnings("unchecked")
    @Override
    protected void buildExcelDocument(Map<String, Object> model, WritableWorkbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception {
        try {
            response.setHeader("content-disposition", "attachment;filename=" + model.get("fileName") + ".xls");
            WritableSheet ws = workbook.createSheet(model.get("mainSheetName").toString(), 0);
            WritableSheet ws1 = workbook.createSheet(model.get("subordinateSheetName").toString(), 1);
            WritableCellFormat wcf = new WritableCellFormat();
            wcf.setAlignment(Alignment.LEFT);
            DateFormat df = new DateFormat(DateFormatter.COMMON_DATE_FORMAT);
            WritableCellFormat dateWcf = new WritableCellFormat(df);
            dateWcf.setAlignment(Alignment.LEFT);
            List<String> itemTitle = (List<String>) model.get("itemTitle");
            constructTableTitle(ws, itemTitle, wcf);
            List<String> itemDetailsTitle = (List<String>) model.get("itemDetailsTitle");
            constructTableTitle(ws1, itemDetailsTitle, wcf);
            Map<Object, List<List<Object>>> itemDetails = (LinkedHashMap<Object, List<List<Object>>>) model.get("itemDetails");
            Map<Object, Integer> map = constructItemDetailsContent(ws1, wcf, dateWcf, itemDetails);
            List<Object> itemList = (List<Object>) model.get("itemList");
            int linkedrows = Integer.parseInt(model.get("linkedrows").toString());
            constructItemSheetContent(ws, ws1, map, itemList, linkedrows);
        } catch (Exception e) {
            logger.debug(e.getMessage());
        }
    }

    @SuppressWarnings("unchecked")
    private void constructItemSheetContent(WritableSheet ws, WritableSheet ws1, Map<Object, Integer> map, List<Object> itemList, int linkedrows) throws RowsExceededException, WriteException {
        WritableCellFormat wcf = new WritableCellFormat();
        wcf.setAlignment(Alignment.LEFT);
        DateFormat df = new DateFormat(DateFormatter.COMMON_DATE_FORMAT);
        WritableCellFormat dateWcf = new WritableCellFormat(df);
        dateWcf.setAlignment(Alignment.LEFT);
        for (int i = 0; i < itemList.size(); i++) {
            List<Object> listtemp = (List<Object>) itemList.get(i);
            for (int j = 0; j < listtemp.size(); j++) {
                if (j == linkedrows && map.containsKey(listtemp.get(j))) {
                    ws.addHyperlink(new WritableHyperlink(j, i + 1, listtemp.get(j).toString(), ws1, 0, map.get(listtemp.get(j))));
                } else {
                    WritableCell writableCell = constructTableContent(i + 1, j, listtemp.get(j), wcf, dateWcf);
                    if (null != writableCell) {
                        ws.addCell(writableCell);
                    }
                }
            }
        }
    }

    private Map<Object, Integer> constructItemDetailsContent(WritableSheet ws1, WritableCellFormat wcf, WritableCellFormat dateWcf, Map<Object, List<List<Object>>> itemDetails) throws WriteException,
            RowsExceededException {
        Map<Object, Integer> map = new HashMap<>();
        int k = 0;
        for (Iterator<Map.Entry<Object, List<List<Object>>>> it = itemDetails.entrySet().iterator(); it.hasNext(); ) {
            Map.Entry<Object, List<List<Object>>> type = (Map.Entry<Object, List<List<Object>>>) it.next();
            WritableCell writableCell = constructTableContent(k + 1, 0, type.getKey(), wcf, dateWcf);
            if (null != writableCell) {
                ws1.addCell(writableCell);
            }
            map.put(type.getKey(), k + 1);
            List<List<Object>> items = type.getValue();
            for (int i = 0; i < items.size(); i++) {
                List<Object> itemDetailsList = items.get(i);
                construct(ws1, wcf, dateWcf, k, itemDetailsList);
                k++;
            }
        }
        return map;
    }

    private void construct(WritableSheet ws1, WritableCellFormat wcf, WritableCellFormat dateWcf, int k, List<Object> itemDetailsList) throws WriteException, RowsExceededException {
        for (int j = 0; j < itemDetailsList.size(); j++) {
            WritableCell writableContentCell = constructTableContent(k + 1, j + 1, itemDetailsList.get(j), wcf, dateWcf);
            if (null != writableContentCell) {
                ws1.addCell(writableContentCell);
            }
        }
    }

    private void constructTableTitle(WritableSheet ws, List<String> itemTitle, WritableCellFormat wcf) throws WriteException, RowsExceededException {
        for (int i = 0; i < itemTitle.size(); i++) {
            Label labelData = new Label(i, 0, String.valueOf(itemTitle.get(i)), wcf);
            ws.addCell(labelData);
            ws.setColumnView(i, 16);
        }
    }

    private WritableCell constructTableContent(int end, int j, Object object, WritableCellFormat wcf, WritableCellFormat wcf1) throws WriteException, RowsExceededException {
        if (null == object) {
            return null;
        }
        if (object instanceof Integer || (Pattern.matches("\\d+", object.toString()) && object.toString().length() < 10)) {
            return new Number(j, end, Integer.parseInt(object.toString()), wcf);
        }
        if (object instanceof Date) {
            return new DateTime(j, end, (Date) object, wcf1);
        }
        return new Label(j, end, String.valueOf(object), wcf);
    }
}
